package com.lottery.system.service.impl;
import com.lottery.system.common.errorcode.ServiceErrorCodeConstants;
import com.lottery.system.common.exception.ServiceException;
import com.lottery.system.common.utils.JacksonUtil;
import com.lottery.system.common.utils.RedisUtil;
import com.lottery.system.controller.param.DrawPrizeParam;
import com.lottery.system.controller.param.WinningRecordParam;
import com.lottery.system.dao.dataobject.*;
import com.lottery.system.dao.mapper.*;
import com.lottery.system.service.DrawPrizeService;

import com.lottery.system.service.dto.WinningRecordDTO;
import com.lottery.system.service.enums.ActivityPrizeStatusEnum;
import com.lottery.system.service.enums.ActivityPrizeTiersEnum;
import com.lottery.system.service.enums.ActivityStatusEnum;
import com.lottery.system.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

import static com.lottery.system.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.lottery.system.common.config.DirectRabbitConfig.ROUTING;

/**
 * @author: xiaoxie
 * create: 2024-09-11 09:24
 * @BelongsProject: lottery-system
 * @BelongsPackage: com.lottery.system.service.impl
 * description: 抽奖接口的实现类
 */
@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {

    private final static Logger logger = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private WinningRecordMapper winningRecordMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private UserMapper userMapper;
    //缓存的前缀
    private final static String CACHE_KEY = "Winning_";
    //缓存的过期时间
    private final static Long CACHE_EXPIRE = 60 * 60 * 24 * 3L;
    /**
     * @description: 异步抽奖接口
     * @author: xiaoxie
     * @date: 2024/9/11 9:27
     * @param: [param]
     * @return: void
     **/
    @Override
    public void drawPrize(DrawPrizeParam param) {
        Map<String,String> map = new HashMap<>();
        //使用uuid作为消息id
        map.put("messageId",String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING,map);
        logger.info("mq消息发送成功：map={}",JacksonUtil.writeValueAsString(map));
    }


    /**
     * @description: 校验抽奖参数
     * @author: xiaoxie
     * @date: 2024/9/14 9:56
     * @param: [param]
     * @return: void
     **/
    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {
        logger.info("checkDrawPrizeParam:DrawPrizeParam{}",JacksonUtil.writeValueAsString(param));
        ActivityDO activityDO = activityMapper.selectActivityById(param.getActivityId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectActivityPrizeByIdAndActivityId(
                                                               param.getActivityId()
                                                                ,param.getPrizeId());
        List<Long> userIds = param.getWinnerList()
                             .stream()
                             .mapToLong(DrawPrizeParam.Winner:: getUserId)
                             .distinct()
                             .boxed().toList();
        logger.info("userIds={}",userIds);
        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectActivityUserListByIds(param.getActivityId(),userIds);
        //校验活动,人员,奖品是否存在
        if(null == activityDO || null == activityPrizeDO
            || null == activityUserDOList || activityUserDOList.size() < param.getWinnerList().size()) {
            logger.info("校验抽奖失败:原因{}"
                    ,ServiceErrorCodeConstants.DRAW_PRIZE_DATA_IS_EXIT.getMsg());
           return false;
        }
        //校验活动是否有效
        if(!activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.RUNNING.name())) {
            logger.info("校验抽奖失败:原因{}"
                    ,ServiceErrorCodeConstants.DRAW_PRIZE_ACTIVITY_STATUS_ERROR.getMsg());
           return false;
        }
        //校验奖品是否有效
        if(!activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.INIT.name())) {
            logger.info("校验抽奖失败:原因{}"
                    ,ServiceErrorCodeConstants.DRAW_PRIZE_PRIZE_STATUS_ERROR.getMsg());
          return false;
        }
        boolean isValid = true;

        for (ActivityUserDO activityUserDO : activityUserDOList) {
            if (!activityUserDO.getStatus().equalsIgnoreCase(ActivityUserStatusEnum.INIT.name())) {
                logger.info("校验抽奖失败:原因{}", ServiceErrorCodeConstants.DRAW_PRIZE_USER_STATUS_ERROR.getMsg());
                isValid = false;
                break; // 发现第一个无效用户后立即退出循环
            }
        }
        if (!isValid) {
            return false;
        }
        //校验奖品数量是否等于中奖人员数量
        if(activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()) {
            logger.info("校验抽奖失败:原因{}"
                    ,ServiceErrorCodeConstants.DRAW_PRIZE_AND_USER_COUNT__ERROR.getMsg());
            return false;
        }
        return true;
    }

    /**
     * @description: 保存中奖者信息
     * @author: xiaoxie
     * @date: 2024/9/13 10:34
     * @param: [param]
     * @return: void
     **/
    @Override
    public List<WinningRecordDO> savaActivityWinningInfo(DrawPrizeParam param) {
        //先获取用户表,活动表,奖品表,活动奖品表信息
        ActivityDO activityDO = activityMapper.selectActivityById(param.getActivityId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectActivityPrizeByIdAndActivityId(param.getActivityId(), param.getPrizeId());
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        List<UserDo> userDoList = userMapper.getUserInfoByIds(
                param.getWinnerList()
                        .stream()
                        .map(DrawPrizeParam.Winner :: getUserId)
                        .toList()
        );
        //校验一下数据库数据
        checkParam(activityDO,activityPrizeDO,prizeDO,userDoList);
        List<WinningRecordDO> wRecordList;
        //整合数据
        wRecordList = userDoList.stream()
                                .map(userDo -> {
                                    WinningRecordDO winningRecordDO = new WinningRecordDO();
                                    winningRecordDO.setActivityId(activityPrizeDO.getActivityId());
                                    winningRecordDO.setActivityName(activityDO.getActivityName());
                                    winningRecordDO.setPrizeId(prizeDO.getId());
                                    winningRecordDO.setPrizeName(prizeDO.getName());
                                    winningRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                                    winningRecordDO.setWinnerId(userDo.getId());
                                    winningRecordDO.setWinnerEmail(userDo.getEmail());
                                    winningRecordDO.setWinnerName(userDo.getUserName());
                                    winningRecordDO.setWinnerPhone(userDo.getPhoneNumber());
                                    winningRecordDO.setWinningTime(param.getWinningTime());
                                    return winningRecordDO;
                                }).toList();

        //先保存数据库信息
        winningRecordMapper.batchInsert(wRecordList);
        //缓存信息奖品的维度
        cacheWinningRecord(param.getActivityId()+"_"+ param.getPrizeId()
                                          ,wRecordList);
        //缓存信息活动的维度
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
           List<WinningRecordDO> allList = winningRecordMapper.selectById(activityDO.getId());
            cacheWinningRecord(String.valueOf(activityDO.getId()),allList);
        }
        return wRecordList;
    }


    /**
     * @description: 回滚中奖记录
     * @author: xiaoxie
     * @date: 2024/9/15 21:50
     * @param: [activityId, prizeId]
     * @return: void
     **/
    @Override
    public void rollBackWinnerRecords(Long activityId,Long prizeId) {
        if(null == activityId) {
            logger.warn("活动id为null,无法回滚");
            return;
        }
        //删除数据库表
        winningRecordMapper.delete(activityId,prizeId);
        //删除缓存 活动维度(由于奖品被删除,所以活动必需被删除) 奖品维度
        if(prizeId != null) {
            deleteCacheWinningRecord(activityId+"_"+prizeId);
        }
        //无论从奖品维度的缓存需不要要删除都必需删除活动维度的缓存
        deleteCacheWinningRecord(String.valueOf(activityId));

    }
    /**
     * @description: 获取中奖记录
     * @author: xiaoxie
     * @date: 2024/9/16 19:36
     * @param: [param]
     * @return: java.util.List<com.lottery.system.service.dto.WinningRecordDTO>
     **/
    @Override
    public List<WinningRecordDTO> getRecords(WinningRecordParam param) {
        //先判断prizeId是否为空
        String key = null == param.getPrizeId() ? String.valueOf(param.getActivityId())
                                               :  param.getActivityId()+ "_" + param.getPrizeId();
        //先查询redis
        List<WinningRecordDO> winningRecord = getWinningRecord(key);
        if(!winningRecord.isEmpty()) {
            return convertWinningRecordDTO(winningRecord);
        }
        //如果没有就查询数据库
        List<WinningRecordDO> winningList = winningRecordMapper.selectByAOrPId(param.getActivityId()
                                                               ,param.getPrizeId());
        //缓存在redis中
        cacheWinningRecord(key,winningList);

        return convertWinningRecordDTO(winningList);
    }

    private List<WinningRecordDTO> convertWinningRecordDTO(
            List<WinningRecordDO> winningRecord) {
        return winningRecord.stream()
                            .map(
                                 wRecord->{
                                     WinningRecordDTO result = new WinningRecordDTO();
                                     result.setWinnerId(wRecord.getWinnerId());
                                     result.setWinnerName(wRecord.getWinnerName());
                                     result.setPrizeName(wRecord.getPrizeName());
                                     result.setPrizeTier(ActivityPrizeTiersEnum.forName(
                                                         wRecord.getPrizeTier()));
                                     result.setWinningTime(wRecord.getWinningTime());
                                     return result;
                                 }
                            ).toList();
    }

    /**
     * @description: 删除缓存
     * @author: xiaoxie
     * @date: 2024/9/15 21:52
     * @param: [key]
     * @return: void
     **/
    private void deleteCacheWinningRecord(String key) {
        try{
            //判断key是否存在
            if(redisUtil.hasKey(CACHE_KEY+key)) {
                redisUtil.del(CACHE_KEY+key);
            }
        }catch (Exception e){
            logger.warn("删除缓存信息异常: key:{}",CACHE_KEY+key);
        }

        //存在在删除
    }

    /**
     * @description: 缓存中奖记录
     * @author: xiaoxie
     * @date: 2024/9/13 11:32
     * @param: [key, wRecordList, cacheExpire]
     * @return: void
     **/
    private void cacheWinningRecord(String key, List<WinningRecordDO> wRecordList) {
        if(null == key || null == wRecordList || wRecordList.isEmpty()) {
            logger.warn("缓存信息异常: key:{},value:{}",CACHE_KEY+key,wRecordList);
            return;
        }
        try {
            redisUtil.setTimeout(CACHE_KEY+key,JacksonUtil.writeValueAsString(wRecordList),CACHE_EXPIRE);
        }catch (Exception e) {
            logger.warn("cacheWinningRecordByPrize 缓存中奖记录失败: key:{},value:{}",CACHE_KEY+key,wRecordList);
        }
    }
    /**
     * @description: 获取缓存中奖记录
     * @author: xiaoxie
     * @date: 2024/9/13 11:41
     * @param: [key]
     * @return: java.util.List<com.lottery.system.dao.dataobject.WinningRecordDO>
     **/
    private List<WinningRecordDO> getWinningRecord(String key) {
        try {
            if (!StringUtils.hasText(key)) {
                logger.warn("要从缓存中查询中奖记录的key为空！");
                return List.of();
            }
            String str = redisUtil.get(CACHE_KEY + key);
            if (!StringUtils.hasText(str)) {
                return List.of();
            }

            return JacksonUtil.readListValue(str, WinningRecordDO.class);
        } catch (Exception e) {
            logger.error("从缓存中查询中奖记录异常！key:{}",CACHE_KEY + key);
            return List.of();
        }
    }


    /**
     * @description: 校验数据库信息
     * @author: xiaoxie
     * @date: 2024/9/13 11:00
     * @param: [activityDO, activityPrizeDO, prizeDO, userDoList]
     * @return: void
     **/
    private void checkParam(ActivityDO activityDO
                          , ActivityPrizeDO activityPrizeDO
                          , PrizeDO prizeDO
                          , List<UserDo> userDoList) {
        if(null == activityDO || null == activityPrizeDO
                              || null == prizeDO
                              || null == userDoList) {
            logger.warn(ServiceErrorCodeConstants.DRAW_PRIZE_DATA_IS_EXIT.getMsg());
            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_DATA_IS_EXIT);
        }
        if(userDoList.size() != activityPrizeDO.getPrizeAmount()) {
            logger.warn(ServiceErrorCodeConstants.DRAW_PRIZE_AND_USER_COUNT__ERROR.getMsg());
            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_AND_USER_COUNT__ERROR);
        }

    }
}
